﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureVerticalColumn.h"
#include "ColumnParameterBehavior.h"
#include "DbObjectUtils.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "GmStructureDefaultElementParametersCustomizer.h"
#include "ICalculator.h"
#include "IDocument.h"
#include "IElementModelShape.h"
#include "IElementMoveContext.h"
#include "IElementParameters.h"
#include "IElementParentReporter.h"
#include "IElementPosition.h"
#include "IElementStatus.h"
#include "IElementTransformationComponent.h"
#include "IExternalDataComponent.h"
#include "IFamily.h"
#include "IGraphicsElementShape.h"
#include "IInstance.h"
#include "IInstanceType.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IParameterValueElementId.h"
#include "IPositionTwoAssociatedPlanes.h"
#include "IRegenerator.h"
#include "StructureColumnHeightParameterUpdater.h"
#include "StructureColumnInput.h"
#include "StructureGeneralParameterValidator.h"
#include "StructureInstanceLevelUtils.h"

#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

namespace gcmp
{
    class StructureVerticalColumnElementParametersCustomizer : 
        public GmStructureDefaultElementParametersCustomizer
    {
        DBOBJECT_DATA_BEGIN(gcmp, StructureVerticalColumnElementParametersCustomizer, 
            gcmp::GmStructureDefaultElementParametersCustomizer, 
            D34388C2-3AB5-4172-827C-EC710ED5F48B, gmstructure)
        DBOBJECT_DATA_END
    public:
        StructureVerticalColumnElementParametersCustomizer(IElement* pElement);

        virtual bool IsPresetParameter(const UniIdentity& paramDefUid) const override;

        virtual void FilterParameterValues(const UniIdentity& paramDefUid, 
            std::vector<OwnerPtr<IParameterValueStorage>>& values) const override;
    };
}

DBOBJECT_DATA_DEFINE(StructureVerticalColumnElementParametersCustomizer)
{
    SetOwnerElement(nullptr);
}

StructureVerticalColumnElementParametersCustomizer::StructureVerticalColumnElementParametersCustomizer(
    IElement* pElement) : GmStructureDefaultElementParametersCustomizer(pElement)
{
}

bool StructureVerticalColumnElementParametersCustomizer::IsPresetParameter(const UniIdentity& paramDefUid) const
{
    if (paramDefUid.HasSameValue(PARAMETER_DEF(ElementAssociatedLevelBuiltInParameter)->GetUid()) ||
        paramDefUid.HasSameValue(PARAMETER_UID(BuildingStoreyBuiltInParameter)) ||
        paramDefUid.HasSameValue(PARAMETER_DEF(TopAssociatedLevelBuiltInParameter)->GetUid()) ||
        paramDefUid.HasSameValue(PARAMETER_DEF(BottomOffsetBuiltInParameter)->GetUid()) ||
        paramDefUid.HasSameValue(PARAMETER_DEF(TopOffsetBuiltInParameter)->GetUid()) ||
        paramDefUid.HasSameValue(PARAMETER_DEF(ColumnHeightBuiltInParameter)->GetUid()) ||
        paramDefUid.HasSameValue(PARAMETER_DEF(ConcreteStrengthGradeBuiltInParameter)->GetUid())
        )
    {
        return true;
    }
    return false;
}

void StructureVerticalColumnElementParametersCustomizer::FilterParameterValues(
    const UniIdentity& paramDefUid, std::vector<OwnerPtr<IParameterValueStorage>>& values) const
{
    // 柱的定位方式为底标高+顶标高或底标高+柱高，其默认顶标高为Invalid，默认下拉列表默认有“< 空 >”
    if (paramDefUid.HasSameValue(PARAMETER_UID(TopAssociatedLevelBuiltInParameter)))
    {
        values.push_back(IParameterValueElementId::Create(ElementId::InvalidID));
    }
}

DEFINE_STRUCTURE_INSTANCE_BASE_DATA_METHODS(StructureVerticalColumn, BaseData);

DBOBJECT_DATA_DEFINE(StructureVerticalColumn)
{
}

IStructureVerticalColumn* StructureVerticalColumn::Create(const StructureColumnInput* pInput)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInput, L"pInput为空",L"GDMPLab",L"2024-03-30");

    IInstance* pInstance = StructureVerticalColumn::CreateVerticalColumn(pInput);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");

    IStructureVerticalColumn* pVerticalColumn = StructureVerticalColumn::Get(pInstance);

    {
        IElementParameters * pElementParameters = pInstance->GetElementParameters();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementParameters, L"pInstance的参数行为为空？",L"GDMPLab",L"2024-03-30");
        pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(NotUserVisiableColumnAngleBuiltInParameterOverride));
        pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(NotUserVisiableSectionRotateAngleBuiltInParameterOverride));
        pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(GeneralHeightBuiltInParameterOverride, pInstance, PARAMETER_UID(ColumnHeightBuiltInParameter)));

        // TODO: 
        // 先删后加的目的是演示平台具有动态删除、增加单个参数定制行为的能力，以及覆盖API。
        // 后面有合适的其它例子这里可以删除。
        {
            pElementParameters->RemoveIndividualParameterOverride(PARAMETER_UID(SectionRotateAngleBuiltInParameter));
            pElementParameters->RemoveIndividualParameterOverride(PARAMETER_UID(ColumnAngleBuiltInParameter));
            pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(NotUserVisiableColumnAngleBuiltInParameterOverride));
            pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(NotUserVisiableSectionRotateAngleBuiltInParameterOverride));
        }

        //{
        //    //TODO: 参数验证器， 如果不满足要求请删除 
        //    pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(ParameterValidatorCustomizedBehavior, PARAMETER_UID(ColumnHeightBuiltInParameter)));
        //    pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(ParameterValidatorCustomizedBehavior, PARAMETER_UID(TopOffsetBuiltInParameter)));
        //    pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(ParameterValidatorCustomizedBehavior, PARAMETER_UID(BottomOffsetBuiltInParameter)));
        //    pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(ParameterValidatorCustomizedBehavior, PARAMETER_UID(TopAssociatedLevelBuiltInParameter)));
        //    pElementParameters->AddIndividualParameterOverride(NEW_AS_OWNER_PTR(ParameterValidatorCustomizedBehavior, PARAMETER_UID(ElementAssociatedLevelBuiltInParameter)));
        //}
    }

    return pVerticalColumn;
}

const IStructureVerticalColumn *gcmp::StructureVerticalColumn::Get(const IInstance *pInstance)
{
    DBG_WARN_AND_RETURN_UNLESS(pInstance, nullptr, L"pInstance为空",L"GDMPLab",L"2024-03-30");
    if (const IExternalDataComponent * pExternalDataComponent = pInstance->GetExternalDataComponent())
    {
        const IExternalData *pData = pExternalDataComponent->FindExternalData(StructureVerticalColumn::GetStaticClassSchema()->GetName());
        DBG_WARN_AND_RETURN_UNLESS(pData, nullptr, L"结构构件应该会有ExternalData",L"GDMPLab",L"2024-03-30");
        return quick_cast<StructureVerticalColumn>(pData);
    }
    return nullptr;
}

IStructureVerticalColumn *gcmp::StructureVerticalColumn::Get(IInstance *pInstance)
{
    DBG_WARN_AND_RETURN_UNLESS(pInstance, nullptr, L"pInstance为空",L"GDMPLab",L"2024-03-30");
    if (IExternalDataComponent * pExternalDataComponent = pInstance->GetExternalDataComponent())
    {
        IExternalData *pData = pExternalDataComponent->FindExternalData(StructureVerticalColumn::GetStaticClassSchema()->GetName());
        DBG_WARN_AND_RETURN_UNLESS(pData, nullptr, L"结构构件应该会有ExternalData",L"GDMPLab",L"2024-03-30");
        return quick_cast<StructureVerticalColumn>(pData);
    }
    return nullptr;
}

IInstance* StructureVerticalColumn::CreateVerticalColumn(const StructureColumnInput* pInput)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInput, L"pInput为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInput->IsVerticalColumn, L"pInput 创建参数有误",L"GDMPLab",L"2024-03-30");

    IDocument* pDoc = pInput->Document;
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");

    //创建实例
    IInstanceType* pInstanceType = quick_cast<IInstanceType>(pDoc->GetElement(pInput->InstanceTypeId));
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstanceType, L"pInstanceType为空",L"GDMPLab",L"2024-03-30");

    IInstance* pInstance = pInstanceType->CreateInstance(L"直柱实例", pInput->CanBeShared, pInput->CreationOption, pInput->IsCreateGeometryRelationshipComponent);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");

    //设置instance的Unidentity
    if (pInput->categoryUid.IsValid())
    {
        pInstance->GetBasicInformation()->SetCategoryUid(pInput->categoryUid);
    }

    //挂externalData
    IStructureVerticalColumn* pVerticalColumn = StructureVerticalColumn::RegisterExternalData(pInstance, pInput);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pVerticalColumn, L"pVerticalColumn为空",L"GDMPLab",L"2024-03-30");

    ElementId associatedPlaneId = pInput->AssociatedPlaneId;
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(associatedPlaneId.IsValid(), L"associatedPlaneId无效",L"GDMPLab",L"2024-03-30");
    ElementId topAssociatedPlaneId = pInput->TopAssociatedPlaneId;

    //设置所属楼层
    if (pInput->StoreyId.IsValid())
    {
        bool bOk = StructureInstanceLevelUtils::CreateInstanceStoreyLevel(pDoc,pInstance->GetElementId(), pInput->StoreyId);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"SetStoreyLevel失败",L"GDMPLab",L"2024-03-30");
    }
    else
    {
        const ILevel* pStoreyLevel = StructureInstanceLevelUtils::GetColumnStoreyLevelFromPositioningLevel(pDoc, associatedPlaneId);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pStoreyLevel, L"pStoreyLevel为空",L"GDMPLab",L"2024-03-30");
        bool bOk = StructureInstanceLevelUtils::CreateInstanceStoreyLevel(pInstance, pStoreyLevel);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"SetStoreyLevel失败",L"GDMPLab",L"2024-03-30");
    }


    // 设置定位件
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInput->PositionPoints.size()==1, L"PositionPoints非法",L"GDMPLab",L"2024-03-30");
    Vector3d positionPoint = pInput->PositionPoints.front();
    IElementPosition::CreatePoint2dOnTwoAssociatedPlanesPositionBehavior(pInstance, associatedPlaneId, positionPoint, Vector3d::UnitX, Vector3d::UnitY, topAssociatedPlaneId);

    IElementPosition* pPositionBehavior = pInstance->GetElementPosition();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPositionBehavior, L"pPositionBehavior为空",L"GDMPLab",L"2024-03-30");

    IPositionTwoAssociatedPlanes *pPositionTwoAssociatedPlanes = quick_cast<IPositionTwoAssociatedPlanes>(pPositionBehavior->GetPositionAssociatedPlane());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPositionTwoAssociatedPlanes, L"pPositionTwoAssociatedPlanes为空",L"GDMPLab",L"2024-03-30");

    pPositionTwoAssociatedPlanes->SetBottomOffset(pInput->AssociatedPlaneOffset);
    if (topAssociatedPlaneId.IsValid())
    {
        pPositionTwoAssociatedPlanes->SetTopOffset(pInput->TopOffset);
    }
    else
    {
        //设置高度参数
        IElementParameters *pParam = pInstance->GetElementParameters();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pParam != nullptr, L"无效pParam",L"GDMPLab",L"2024-03-30");

        OwnerPtr<IParameter> heightParam = pParam->GetParameterByUid(PARAMETER_UID(ColumnHeightBuiltInParameter));
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(IParameter::IsValidParameter(heightParam), L"没有高度参数",L"GDMPLab",L"2024-03-30");

        heightParam->SetValueAsDouble(pInput->Height);
        pParam->SetParameter(heightParam.get());
    }

    //添加高度参数更新器
    IElementPosition* pPosBehavior = pInstance->GetElementPosition();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pPosBehavior, L"无效pPositionBehavior",L"GDMPLab",L"2024-03-30");
    OwnerPtr<IInstanceFamilyParametersUpdater> opParametersUpdater = NEW_AS_OWNER_PTR(StructureColumnHeightParameterUpdater);
    pPosBehavior->SetParametersUpdater(TransferOwnership(opParametersUpdater));

    //根据锚点定位移动形体(锚点位于中心时无需处理)
    const InstanceAnchorPointType anchorType = pInput->AnchorType;
    IElementTransformationComponent *pElementTransformationComponent = pInstance->GetElementTransformationComponent();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pElementTransformationComponent, L"pElementTransformationComponent为空",L"GDMPLab",L"2024-03-30");

    if (anchorType != InstanceAnchorPointType::CenterAnchorPointType)
    {
        bool bOk = pDoc->GetRegenerator()->RegenerateElement(pInstance->GetElementId());

        if (pInstance->GetElementModelShape() && pInstance->GetElementModelShape()->GetGraphicsElementShape())
        {
            Box3d box = pInstance->GetElementModelShape()->GetGraphicsElementShape()->GetBox();
            double halfX = (box.GetMaxPoint().X() - box.GetMinPoint().X()) / 2;
            double halfY = (box.GetMaxPoint().Y() - box.GetMinPoint().Y()) / 2;
            Vector3d moveVec;
            switch (anchorType)
            {
            case InstanceAnchorPointType::LeftTopAnchorPointType:
                moveVec.Set(halfX, -halfY, 0);
                break;
            case InstanceAnchorPointType::RightTopAnchorPointType:
                moveVec.Set(-halfX, -halfY, 0);
                break;
            case InstanceAnchorPointType::LeftBottomAnchorPointType:
                moveVec.Set(halfX, halfY, 0);
                break;
            case InstanceAnchorPointType::RightBottomAnchorPointType:
                moveVec.Set(-halfX, halfY, 0);
                break;
            default:
                DBG_WARN(L"未识别类型",L"GDMPLab",L"2024-03-30");
                break;
            }
            if (!box.IsEmpty() && !moveVec.IsZero())
            {
                OwnerPtr<IElementMoveContext> opMoveContext = IElementMoveContext::CreateElementMoveContext(pInstance->GetDocument(), pInstance->GetElementId(), moveVec);
                DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opMoveContext, L"MoveContext创建失败",L"GDMPLab",L"2024-03-30");
                pElementTransformationComponent->Translate(*opMoveContext);
            }
        }
    }

    //旋转
    if (!MathUtils::IsEqual(pInput->RotateAngle, 0, Constants::ANGLE_EPS))
    {
        pElementTransformationComponent->Rotate(positionPoint, Vector3d::UnitZ, pInput->RotateAngle);
    }

    //属性面板行为
    if (pInstance->GetElementParameters())
    {
        OwnerPtr<IElementParametersCustomizer> opNewCustomizer = 
            NEW_AS_OWNER_PTR(StructureVerticalColumnElementParametersCustomizer, pInstance);
        GmStructureDefaultElementParametersCustomizer::SetElementParametersCustomizer(
            pInstance, TransferOwnership(opNewCustomizer));
    }

    //可见性
    IElementStatus* pStatus = pInstance->GetStatus();
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pStatus, L"无法获取状态集",L"GDMPLab",L"2024-03-30");
    pStatus->SetIsVisible(pInput->Visible);

    bool bOk = pDoc->GetRegenerator()->RegenerateElement(pInstance->GetElementId());
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(bOk, L"RegenerateElement失败",L"GDMPLab",L"2024-03-30");

    return pInstance;
}
IStructureVerticalColumn* StructureVerticalColumn::RegisterExternalData(IInstance* pInstance, const StructureColumnInput* pInput)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInput, L"pInput为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<StructureVerticalColumn> opVerticalColumn = NEW_AS_OWNER_PTR(StructureVerticalColumn);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opVerticalColumn, L"opVerticalColumn为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<StructureColumn> opBaseData = StructureColumn::Create(pInstance, pInput);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opBaseData, L"opBaseData为空",L"GDMPLab",L"2024-03-30");
    opVerticalColumn->SetBaseData__(TransferOwnership(opBaseData));

    StructureVerticalColumn* pVerticalColumn = opVerticalColumn.get();
    if (IExternalDataComponent * pExternalDataComponent = pInstance->GetExternalDataComponent())
    {
        pExternalDataComponent->RegisterExternalData(StructureVerticalColumn::GetStaticClassSchema()->GetName(), TransferOwnership(opVerticalColumn));
    }
    return pVerticalColumn;
}


void gcmp::StructureVerticalColumn::UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds)
{
    return GetBaseDataFW__()->UpdateForWeakParentDeletion(deletedElementIds);
}

void gcmp::StructureVerticalColumn::ReportParents(IElementParentReporter& reporter) const
{
    GetBaseData__()->ReportParents(reporter);
}

void gcmp::StructureVerticalColumn::GetCalculators(ICalculatorCollection * calculators) const
{
    GetBaseData__()->GetCalculators(calculators);
}

void gcmp::StructureVerticalColumn::ReportParameterDefinitions(std::vector<int>* pParamDefIds) const
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pParamDefIds != nullptr, L"pParamDefUids为空",L"GDMPLab",L"2024-03-30");
    GetBaseData__()->ReportParameterDefinitions(pParamDefIds);
}

OwnerPtr<IParameter> gcmp::StructureVerticalColumn::GetNativeParameter(int paramDefId) const
{
    OwnerPtr<IParameter> ret=GetBaseData__()->GetNativeParameter(paramDefId);
    if (IParameter::IsValidParameter(ret))
    {
        return ret;
    }

    return nullptr;
}

bool gcmp::StructureVerticalColumn::SetNativeParameter(const IParameter *param, std::wstring* errorMsg/* = nullptr*/)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(param, L"param is null.",L"GDMPLab",L"2024-03-30");

    return GetBaseDataFW__()->SetNativeParameter(param, errorMsg);
}

bool gcmp::StructureVerticalColumn::IsParameterModifiable(int paramDefId) const
{
    return GetBaseData__()->IsParameterModifiable(paramDefId);
}

void gcmp::StructureVerticalColumn::Report(const gcmp::ICopyContext & copyContext, gcmp::ICopyStrategyReporter& reporter) const
{
    GetBaseData__()->Report(copyContext, reporter);
}

double gcmp::StructureVerticalColumn::GetVolume() const
{
    return GetBaseData__()->GetVolume();
}

double gcmp::StructureVerticalColumn::GetHeight() const
{
    return GetBaseData__()->GetHeight();
}

bool StructureVerticalColumn::SetOwnerElement(IElement* pOwnerElement)
{
    return m_BaseData->SetOwnerElement(pOwnerElement);
}

IElement* StructureVerticalColumn::GetOwnerElement()
{
    return GetBaseDataFW__()->GetOwnerElement();
}

const IElement* StructureVerticalColumn::GetOwnerElement() const
{
    return GetBaseData__()->GetOwnerElement();
}

IDocument* StructureVerticalColumn::GetDocument() const
{
    return GetBaseData__()->GetDocument();
}

ElementId StructureVerticalColumn::GetOwnerElementId() const
{
    return GetBaseData__()->GetElementId();
}

gcmp::NdbObject* StructureVerticalColumn::GetTopOwnerObject() const
{
    const StructureColumn* pBaseData = GetBaseData__().get();
    if (!pBaseData)
        return nullptr;
    return pBaseData->GetTopOwnerObject();
}
